package com.lucanjun.cn.springboot.multipledatasources.common.utils;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * @author lucanjun
 * @version 1.0.0
 * @title StringRedisUtil
 * @create 2024-12-19 16:55:19
 * @description StringRedis工具类
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class StringRedisUtil {

    /**
     * redisTemplate.opsForValue();　　//操作字符串
     * redisTemplate.opsForHash();　　 //操作hash
     * redisTemplate.opsForList();　　 //操作list
     * redisTemplate.opsForSet();　　  //操作set
     * redisTemplate.opsForZSet();　 　//操作有序set
     */

    private final StringRedisTemplate stringRedisTemplate;

    public final Long defaultTimeOut = 3600L;
    public final TimeUnit defaultTimeUnit = TimeUnit.SECONDS;

    /**
     * 设置过期时间
     * 使用默认时长：3600L
     * 使用默认时间单位：秒
     *
     * @param key
     */
    public void setExpire(String key) {
        stringRedisTemplate.expire(key, defaultTimeOut, defaultTimeUnit);
    }

    /**
     * 设置过期时间
     * 使用默认时间单位：秒
     *
     * @param key
     * @param timeout
     */
    public void setExpire(String key, Long timeout) {
        stringRedisTemplate.expire(key, timeout, defaultTimeUnit);
    }

    /**
     * 设置过期时间
     *
     * @param key
     * @param timeout
     * @param timeUnit
     */
    public void setExpire(String key, Long timeout, TimeUnit timeUnit) {
        stringRedisTemplate.expire(key, timeout, timeUnit);
    }

    //redisTemplate.opsForValue(); --- 操作字符串-----------------------------------------------------------------------------------

    /**
     * 保存redis方法，不设置时长
     *
     * @param key
     * @param value
     */
    public void setNotExpire(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    /**
     * 保存redis方法
     * 使用默认时长：3600L
     * 使用默认时间单位：秒
     *
     * @param key
     * @param value
     */
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
        stringRedisTemplate.expire(key, defaultTimeOut, defaultTimeUnit);
    }

    /**
     * 保存redis方法
     * 使用默认时间单位：秒
     *
     * @param key
     * @param value
     * @param timeout
     */
    public void set(String key, String value, Long timeout) {
        stringRedisTemplate.opsForValue().set(key, value);
        stringRedisTemplate.expire(key, timeout, defaultTimeUnit);
    }

    /**
     * 保存redis方法
     *
     * @param key
     * @param value
     * @param timeout
     * @param timeUnit
     */
    public void set(String key, String value, Long timeout, TimeUnit timeUnit) {
        stringRedisTemplate.opsForValue().set(key, value);
        if (null != timeout) {
            if (null == timeUnit) {
                timeUnit = defaultTimeUnit;
            }
            stringRedisTemplate.expire(key, timeout, timeUnit);
        }
    }

    /**
     * 根据key获取redis的方法
     *
     * @param key
     * @return
     */
    public String get(String key) {
        String value = stringRedisTemplate.opsForValue().get(key);
        return value;
    }

    /**
     * 根据key删除redis的方法
     *
     * @param key
     */
    public Boolean delete(String key) {
        Boolean delete = stringRedisTemplate.delete(key);
        return delete;
    }

    /**
     * 删除所有的redis的方法
     *
     * @return
     */
    public Long deleteAll() {
        Set<String> keys = stringRedisTemplate.keys("*");
        Long delete = stringRedisTemplate.delete(keys);
        return delete;
    }

    /**
     * 根据key获取redis过期时间
     *
     * @param key
     * @return
     */
    public long getExpire(String key) {
        Long expire = stringRedisTemplate.getExpire(key);
        return expire;
    }

    /**
     * 根据key查询redis是否存在
     *
     * @param key
     * @return
     */
    public Boolean hasKey(String key) {
        Boolean flag = stringRedisTemplate.hasKey(key);
        return flag;
    }

    /**
     * 查询缓存中的key
     *
     * @param key
     * @return
     */
    public Set<String> queryKeys(String key) {
        String oriKey = "*";
        if (StringUtils.isNotBlank(key)) {
            oriKey = oriKey + key + oriKey;
        }
        Set<String> keys = stringRedisTemplate.keys(oriKey);
        return keys;
    }

    /**
     * 加减计数
     *
     * @param key
     * @param add
     * @return
     */
    public Long increment(String key, long add) {
        return stringRedisTemplate.opsForValue().increment(key, add);
    }

    //redisTemplate.opsForHash(); --- 操作hash-----------------------------------------------------------------------------------

    /**
     * void put(H key, HK hashKey, HV value);
     * 新增hashMap值
     * <p>
     * key 为Redis的key
     * hashKey 为key对应的map值的key
     * value 为key对应的map值的值
     * hashKey相同替换value
     */
    public void hashPut(String key, String hashKey, String value) {
        stringRedisTemplate.opsForHash().put(key, hashKey, value);
    }

    /**
     * HV get(H key, Object hashKey);
     * 获取key对应的map中，key为hashKey的map的对应的值
     */
    public Object hashGet(String key, String hashKey) {
        Object o = stringRedisTemplate.opsForHash().get(key, hashKey);
        return o;
    }

    /**
     * Map<HK, HV> entries(H key);
     * 获取key对应的所有map键值对
     */
    public Map<Object, Object> hashEntries(String key) {
        Map<Object, Object> entries = stringRedisTemplate.opsForHash().entries(key);
        return entries;
    }

    /**
     * Set<HK> keys(H key);
     * 获取key对应的map中所有的键
     */
    public Set<Object> hashKeys(String key) {
        Set<Object> keys = stringRedisTemplate.opsForHash().keys(key);
        return keys;
    }

    /**
     * List<HV> values(H key);
     * 获取key对应的map中所有的值
     */
    public List<Object> hashValues(String key) {
        List<Object> values = stringRedisTemplate.opsForHash().values(key);
        return values;
    }

    /**
     * Boolean hasKey(H key, Object hashKey);
     * 判断key对应的map中是否有指定的键
     */
    public Boolean hashHasKey(String key, String hashKey) {
        Boolean aBoolean = stringRedisTemplate.opsForHash().hasKey(key, hashKey);
        return aBoolean;
    }

    /**
     * Long size(H key);
     * 获取key对应的map的长度
     */
    public Long hashSize(String key) {
        Long size = stringRedisTemplate.opsForHash().size(key);
        return size;
    }

    /**
     * Boolean putIfAbsent(H key, HK hashKey, HV value);
     * 如果key对应的map不存在，则新增到map中，存在则不新增也不覆盖
     */
    public Boolean hashPutIfAbsent(String key, String hashKey, String value) {
        Boolean putIfAbsent = stringRedisTemplate.opsForHash().putIfAbsent(key, hashKey, value);
        return putIfAbsent;
    }

    /**
     * void putAll(H key, Map<? extends HK, ? extends HV> m);
     * 直接以map集合的方式添加key对应的值
     * <p>
     * map中key已经存在，覆盖替换
     * map中key不存在，新增
     */
    public void hashPutAll(String key, Map<String, String> map) {
        stringRedisTemplate.opsForHash().putAll(key, map);
    }

    /**
     * List<HV> multiGet(H key, Collection<HK> hashKeys);
     * 以集合的方式获取这些键对应的map
     */
    public List<Object> hashMultiGet(String key, List<Object> list) {
        List<Object> objects = stringRedisTemplate.opsForHash().multiGet(key, list);
        return objects;
    }

    /**
     * Long lengthOfValue(H key, HK hashKey);
     * 获取指定key对应的map集合中，指定键对应的值的长度
     * 一个汉字3个长度
     */
    public Long hashLengthOfValue(String key, String hashKey) {
        Long aLong = stringRedisTemplate.opsForHash().lengthOfValue(key, hashKey);
        return aLong;
    }

    /**
     * Long increment(H key, HK hashKey, long delta);
     * 使key对应的map中，键var2对应的值以long1自增
     */
    public Long hashIncrement(String key, String hashKey, Long delta) {
        Long increment = stringRedisTemplate.opsForHash().increment(key, hashKey, delta);
        return increment;
    }

    /**
     * Double increment(H key, HK hashKey, double delta);
     * 使key对应的map中，键var2对应的值以double类型d1自增
     */
    public Double hashIncrement(String key, String hashKey, Double delta) {
        Double increment = stringRedisTemplate.opsForHash().increment(key, hashKey, delta);
        return increment;
    }

    /**
     * Cursor<Map.Entry<HK, HV>> scan(H key, ScanOptions options);
     * 匹配获取键值对
     * <p>
     * ScanOptions.NONE为获取全部键对
     * ScanOptions.scanOptions().match(“map1”).build()，匹配获取键位map1的键值对,不能模糊匹配
     */
    public Cursor<Map.Entry<Object, Object>> hashScan(String key, ScanOptions options) {
        Cursor<Map.Entry<Object, Object>> scan = stringRedisTemplate.opsForHash().scan(key, options);
        return scan;
    }

    /**
     * Long delete(H key, Object... hashKeys);
     * 删除key对应的map中的键值对
     */
    public Long hashDelete(String key, Object... hashKeys) {
        Long delete = stringRedisTemplate.opsForHash().delete(key, hashKeys);
        return delete;
    }

    //redisTemplate.opsForList(); --- 操作list-----------------------------------------------------------------------------------

    /**
     * Long leftPush(K key, V value);
     * 从集合左边插入值
     * <p>
     * 值重复无影响
     * 先进先出
     */
    public Long listLeftPush(String key, String value) {
        Long aLong = stringRedisTemplate.opsForList().leftPush(key, value);
        return aLong;
    }

    /**
     * Long leftPush(K key, V pivot, V value);
     * 从集合左边开始在v值后边插入新值v1
     * <p>
     * 在集合中查找v值
     * 有多个v值时，从左边开始查到第一个v值即可（也就是最后插入的v值，靠左的v值），然后在v值左边插入新值v1
     * 不存在v时，不插入新值v1
     */
    public Long listLeftPush(String key, String pivot, String value) {
        Long aLong = stringRedisTemplate.opsForList().leftPush(key, pivot, value);
        return aLong;
    }

    /**
     * Long leftPushAll(K key, V... values);
     * 从左边批量插入新值，顺序插入
     */
    public Long listLeftPushAll(String key, String... values) {
        Long aLong = stringRedisTemplate.opsForList().leftPushAll(key, values);
        return aLong;
    }

    /**
     * Long leftPushAll(K key, Collection<V> values);
     * 从左边批量插入新值(同上)，顺序插入
     */
    public Long listLeftPushAll(String key, List<String> values) {
        Long aLong = stringRedisTemplate.opsForList().leftPushAll(key, values);
        return aLong;
    }

    /**
     * Long leftPushIfPresent(K key, V value);
     * 如果key存在，从左边插入新值
     * <p>
     * key不存在，不新增
     */
    public Long listLeftPushIfPresent(String key, String value) {
        Long aLong = stringRedisTemplate.opsForList().leftPushIfPresent(key, value);
        return aLong;
    }

    /**
     * V leftPop(K key);
     * 默认移除key中最左的一个值，返回被移除的值
     */
    public String listLeftPop(String key) {
        String value = stringRedisTemplate.opsForList().leftPop(key);
        return value;
    }

    /**
     * V leftPop(K key, long timeout, TimeUnit unit);
     * 指定过期时间后删除key中最左的一个值
     */
    public String listLeftPop(String key, Long timeout, TimeUnit timeUnit) {
        String value = stringRedisTemplate.opsForList().leftPop(key, timeout, timeUnit);
        return value;
    }

    /**
     * V rightPopAndLeftPush(K sourceKey, K destinationKey);
     * 移除k1中最右的值，并将移除的值插入k2中最左侧
     * <p>
     * k1和k2不是同一个key时，k1右侧移除，k2左侧插入，k2不存在时则新增一个然后在插入
     * k1和k2是同一个key时，相当于把最右侧的值移到了最左侧
     */
    public String listRightPopAndLeftPush(String sourceKey, String destinationKey) {
        String value = stringRedisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey);
        return value;
    }

    /**
     * V rightPopAndLeftPush(K sourceKey, K destinationKey, long timeout, TimeUnit unit);
     * 指定过期时间后，移除k1中最右的值，并将移除的值插入k2中最左侧（同上）
     * <p>
     * k1和k2不是同一个key时，k1右侧移除，k2左侧插入，k2不存在时则新增一个然后在插入
     * k1和k2是同一个key时，相当于把最右侧的值移到了最左侧
     */
    public String listRightPopAndLeftPush(String sourceKey, String destinationKey, Long timeout, TimeUnit timeUnit) {
        String value = stringRedisTemplate.opsForList().rightPopAndLeftPush(sourceKey, destinationKey, timeout, timeUnit);
        return value;
    }

    /**
     * Long rightPush(K key, V value);
     * 从右侧插入新值
     */
    public Long listRightPush(String key, String value) {
        Long aLong = stringRedisTemplate.opsForList().rightPush(key, value);
        return aLong;
    }

    /**
     * Long rightPush(K key, V pivot, V value);
     * 从右查找v1，并在v1右侧插入新值v2
     * <p>
     * v1有多个，只找到最靠右的一个即可
     * v1没有，不插入
     */
    public Long listRightPush(String key, String pivot, String value) {
        Long aLong = stringRedisTemplate.opsForList().rightPush(key, pivot, value);
        return aLong;
    }

    /**
     * Long rightPushAll(K key, V... values);
     * 从右侧批量插入
     */
    public Long listRightPushAll(String key, String... value) {
        Long aLong = stringRedisTemplate.opsForList().rightPushAll(key, value);
        return aLong;
    }

    /**
     * Long rightPushAll(K key, Collection<V> values);
     * 从右侧批量插入
     */
    public Long listRightPushAll(String key, List<String> values) {
        Long aLong = stringRedisTemplate.opsForList().rightPushAll(key, values);
        return aLong;
    }

    /**
     * Long rightPushIfPresent(K key, V value);
     * 如果key存在，在右侧新插入value，否则不插入
     */
    public Long listRightPushIfPresent(String key, String value) {
        Long aLong = stringRedisTemplate.opsForList().rightPushIfPresent(key, value);
        return aLong;
    }

    /**
     * V rightPop(K key);
     * 默认从最右侧移除一个值
     */
    public String listRightPop(String key) {
        String value = stringRedisTemplate.opsForList().rightPop(key);
        return value;
    }

    /**
     * rightPop(K key, long timeout, TimeUnit unit)
     * 指定过期时间后，从最右侧移除一个值
     */
    public String listRightPop(String key, Long timeout, TimeUnit timeUnit) {
        String value = stringRedisTemplate.opsForList().rightPop(key, timeout, timeUnit);
        return value;
    }

    /**
     * V index(K key, long index);
     * 获取指定位置的值（index从左往右，从0开始）
     * <p>
     * 超过下标，不报错，返回null
     */
    public String listIndex(String key, Long index) {
        String value = stringRedisTemplate.opsForList().index(key, index);
        return value;
    }

    /**
     * Long size(K key);
     * 获取对应key的集合长度
     */
    public Long listSize(String key) {
        Long size = stringRedisTemplate.opsForList().size(key);
        return size;
    }

    /**
     * void set(K key, long index, V value);
     * 在指定坐标位置插入(替换)新值
     * <p>
     * index不存在，报错（ERR index out of range）
     * key不存在，报错（ERR no such key）
     * 从左侧插入
     */
    public void listSet(String key, Long index, String value) {
        stringRedisTemplate.opsForList().set(key, index, value);
    }

    /**
     * void trim(K key, long start, long end);
     * 截取下标long1和long2之间的值，包括long1和long2对应的值，并将其保留为key对应的新值
     * <p>
     * 左侧坐标从0开始，右侧从-1开始
     * <p>
     * 当long1超过坐标时（此时与long2无关），都会截取为空，key会被删除
     * <p>
     * 当long1为负时（此时与long2无关），都会截取为空，key会被删除
     * <p>
     * 当long1为正且在下标存在其中，long2为负数时，只要两个没有重叠，相当于去左去右，保留了中间的部分
     * <p>
     * 当long1为正且在下标存在其中，long2为负数时，只要两个交叉重叠，截取为空，如下图
     */
    public void listTrim(String key, Long start, Long end) {
        stringRedisTemplate.opsForList().trim(key, start, end);
    }

    /**
     * List<V> range(K key, long start, long end);
     * 获取指定下标间的值
     */
    public List<String> listRange(String key, Long start, Long end) {
        List<String> range = stringRedisTemplate.opsForList().range(key, start, end);
        return range;
    }

    /**
     * Long remove(K key, long count, Object value);
     * 从存储在键中的列表中删除等于值的元素的第一个计数事件。
     * <p>
     * count> 0：删除等于从左到右移动的值的第一个元素；
     * count< 0：删除等于从右到左移动的值的第一个元素；
     * count = 0：删除等于value的所有元素。
     */
    public Long listRemove(String key, Long count, Object value) {
        Long remove = stringRedisTemplate.opsForList().remove(key, count, value);
        return remove;
    }

    //redisTemplate.opsForSet(); --- 操作set-----------------------------------------------------------------------------------

    /**
     * Long add(K key, V... values);
     * 向key中批量添加值
     */
    public Long setAdd(String key, String... value) {
        Long setAdd = stringRedisTemplate.opsForSet().add(key, value);
        return setAdd;
    }

    /**
     * Set<V> members(K key);
     * 获取key中的值
     */
    public Set<String> setMembers(String key) {
        Set<String> members = stringRedisTemplate.opsForSet().members(key);
        return members;
    }

    /**
     * Long size(K key);
     * 获取key对应集合的长度
     */
    public Long setSize(String key) {
        Long members = stringRedisTemplate.opsForSet().size(key);
        return members;
    }

    /**
     * V randomMember(K key);
     * 随机获取key对应的集合中的元素
     */
    public String setRandomMember(String key) {
        String members = stringRedisTemplate.opsForSet().randomMember(key);
        return members;
    }

    /**
     * randomMembers(K key, long long1)
     * 随机获取key对应集合中指定个数的元素
     *
     * set本身是不会有重复的值
     * 随机获取可能获取到一样的，所以返回的list集合中可能会有重复的值
     */

    //redisTemplate.opsForZSet(); --- 操作有序set-----------------------------------------------------------------------------------

}
